package findutil

import (
	

	
	
	
)

const (
	wildcard = "*"
)

type findFunc func(tree.Node, *pathexpr.PathExpr, *[]tree.Node)

var findMap = map[string]findFunc{
	xconst.AxisAncestor:         findAncestor,
	xconst.AxisAncestorOrSelf:   findAncestorOrSelf,
	xconst.AxisAttribute:        findAttribute,
	xconst.AxisChild:            findChild,
	xconst.AxisDescendent:       findDescendent,
	xconst.AxisDescendentOrSelf: findDescendentOrSelf,
	xconst.AxisFollowing:        findFollowing,
	xconst.AxisFollowingSibling: findFollowingSibling,
	xconst.AxisNamespace:        findNamespace,
	xconst.AxisParent:           findParent,
	xconst.AxisPreceding:        findPreceding,
	xconst.AxisPrecedingSibling: findPrecedingSibling,
	xconst.AxisSelf:             findSelf,
}

//Find finds nodes based on the pathexpr.PathExpr
func ( tree.Node,  pathexpr.PathExpr) []tree.Node {
	 := []tree.Node{}

	if .Axis == "" {
		findChild(, &, &)
		return 
	}

	 := findMap[.Axis]
	(, &, &)

	return 
}

func findAncestor( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() == tree.NtRoot {
		return
	}

	addNode(.GetParent(), , )
	(.GetParent(), , )
}

func findAncestorOrSelf( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	findSelf(, , )
	findAncestor(, , )
}

func findAttribute( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if ,  := .(tree.Elem);  {
		for ,  := range .GetAttrs() {
			addNode(, , )
		}
	}
}

func findChild( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if ,  := .(tree.Elem);  {
		 := .GetChildren()
		for  := range  {
			addNode([], , )
		}
	}
}

func findDescendent( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if ,  := .(tree.Elem);  {
		 := .GetChildren()
		for  := range  {
			addNode([], , )
			([], , )
		}
	}
}

func findDescendentOrSelf( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	findSelf(, , )
	findDescendent(, , )
}

func findFollowing( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() == tree.NtRoot {
		return
	}
	 := .GetParent()
	 := .GetChildren()
	 := 0
	for  != [] {
		++
	}
	++
	for  < len() {
		findDescendentOrSelf([], , )
		++
	}
	(, , )
}

func findFollowingSibling( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() == tree.NtRoot {
		return
	}
	 := .GetParent()
	 := .GetChildren()
	 := 0
	for  != [] {
		++
	}
	++
	for  < len() {
		findSelf([], , )
		++
	}
}

func findNamespace( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if ,  := .(tree.NSElem);  {
		 := tree.BuildNS()
		for ,  := range  {
			addNode(, , )
		}
	}
}

func findParent( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() != tree.NtRoot {
		addNode(.GetParent(), , )
	}
}

func findPreceding( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() == tree.NtRoot {
		return
	}
	 := .GetParent()
	 := .GetChildren()
	 := len() - 1
	for  != [] {
		--
	}
	--
	for  >= 0 {
		findDescendentOrSelf([], , )
		--
	}
	(, , )
}

func findPrecedingSibling( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	if .GetNodeType() == tree.NtRoot {
		return
	}
	 := .GetParent()
	 := .GetChildren()
	 := len() - 1
	for  != [] {
		--
	}
	--
	for  >= 0 {
		findSelf([], , )
		--
	}
}

func findSelf( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	addNode(, , )
}

func addNode( tree.Node,  *pathexpr.PathExpr,  *[]tree.Node) {
	 := false
	 := .GetToken()

	switch .GetNodeType() {
	case tree.NtAttr:
		 = evalAttr(, .(xml.Attr))
	case tree.NtChd:
		 = evalChd()
	case tree.NtComm:
		 = evalComm()
	case tree.NtElem, tree.NtRoot:
		 = evalEle(, .(xml.StartElement))
	case tree.NtNs:
		 = evalNS(, .(xml.Attr))
	case tree.NtPi:
		 = evalPI()
	}

	if  {
		* = append(*, )
	}
}

func evalAttr( *pathexpr.PathExpr,  xml.Attr) bool {
	if .NodeType == "" {
		if .Name.Space != wildcard {
			if .Name.Space != .NS[.Name.Space] {
				return false
			}
		}

		if .Name.Local == wildcard && .Axis == xconst.AxisAttribute {
			return true
		}

		if .Name.Local == .Name.Local {
			return true
		}
	} else {
		if .NodeType == xconst.NodeTypeNode {
			return true
		}
	}

	return false
}

func evalChd( *pathexpr.PathExpr) bool {
	if .NodeType == xconst.NodeTypeText || .NodeType == xconst.NodeTypeNode {
		return true
	}

	return false
}

func evalComm( *pathexpr.PathExpr) bool {
	if .NodeType == xconst.NodeTypeComment || .NodeType == xconst.NodeTypeNode {
		return true
	}

	return false
}

func evalEle( *pathexpr.PathExpr,  xml.StartElement) bool {
	if .NodeType == "" {
		return checkNameAndSpace(, )
	}

	if .NodeType == xconst.NodeTypeNode {
		return true
	}

	return false
}

func checkNameAndSpace( *pathexpr.PathExpr,  xml.StartElement) bool {
	if .Name.Local == wildcard && .Name.Space == "" {
		return true
	}

	if .Name.Space != wildcard && .Name.Space != .NS[.Name.Space] {
		return false
	}

	if .Name.Local == wildcard && .Axis != xconst.AxisAttribute && .Axis != xconst.AxisNamespace {
		return true
	}

	if .Name.Local == .Name.Local {
		return true
	}

	return false
}

func evalNS( *pathexpr.PathExpr,  xml.Attr) bool {
	if .NodeType == "" {
		if .Name.Space != "" && .Name.Space != wildcard {
			return false
		}

		if .Name.Local == wildcard && .Axis == xconst.AxisNamespace {
			return true
		}

		if .Name.Local == .Name.Local {
			return true
		}
	} else {
		if .NodeType == xconst.NodeTypeNode {
			return true
		}
	}

	return false
}

func evalPI( *pathexpr.PathExpr) bool {
	if .NodeType == xconst.NodeTypeProcInst || .NodeType == xconst.NodeTypeNode {
		return true
	}

	return false
}